package models

import (
	"fmt"
	pagination "github.com/ikaiguang/go-pagination"
	"github.com/jinzhu/gorm"
	"strings"
)

type PageWhereOrder struct {
	Order string
	Where string
	Value []interface{}
}

// gorm err info
const (
	ErrDuplicateKey   = "duplicate key value violates unique" // 也可使用数据库的uniqueKey的indexName来判断
	ErrDuplicateKeyCn = "重复键违反唯一约束"                           // 也可使用数据库的uniqueKey的indexName来判断
)

// IsErrDuplicateKey 数据库uniqueIndex错误
// pg : 重复键违反唯一约束 会中断数据库 事务连接，故：重试机制
func IsErrDuplicateKey(err error) bool {
	if err == nil {
		return false
	}

	// en
	if strings.Contains(err.Error(), ErrDuplicateKey) {
		return true
	}
	// cn
	if strings.Contains(err.Error(), ErrDuplicateKeyCn) {
		return true
	}
	return false
}

// Create
func Create(value interface{}) error {
	return db.Create(value).Error
}

// Save
func Save(value interface{}) error {
	return db.Save(value).Error
}

func Update(where interface{}, value interface{}) error {
	return db.Model(where).Updates(value).Error
}

// Updates
func Updates(where interface{}, value interface{}, out interface{}, selectFields ...[]string) error {
	err := db.Model(where).Updates(value).Error
	if err != nil {
		return err
	}
	if len(selectFields) > 0 {
		return db.Select(selectFields[0]).Find(out).Error
	}
	return db.Find(out).Error
}

// UpdateColumns
func UpdateColumns(where interface{}, value interface{}) error {
	err := db.Model(where).UpdateColumns(value).Error
	return err
}

// Delete
func DeleteByModel(model interface{}) (count int64, err error) {
	db := db.Delete(model)
	err = db.Error
	if err != nil {
		return
	}
	count = db.RowsAffected
	return
}

// Delete
func DeleteByWhere(model, where interface{}) (count int64, err error) {
	db := db.Where(where).Delete(model)
	err = db.Error
	if err != nil {
		return
	}
	count = db.RowsAffected
	return
}

// Delete
func DeleteByID(model interface{}, id int) (count int64, err error) {
	db := db.Where("id=?", id).Delete(model)
	err = db.Error
	if err != nil {
		return
	}
	count = db.RowsAffected
	return
}

// Delete
func DeleteByIDS(model interface{}, ids []uint64) (count int64, err error) {
	db := db.Where("id in (?)", ids).Delete(model)
	err = db.Error
	if err != nil {
		return
	}
	count = db.RowsAffected
	return
}

// First
func FirstByID(out interface{}, id int, selectFields ...[]string) (notFound bool, err error) {
	if len(selectFields) > 0 {
		err = db.Select(selectFields[0]).First(out, id).Error
	} else {
		err = db.First(out, id).Error
	}
	if err != nil {
		notFound = gorm.IsRecordNotFoundError(err)
	}
	return
}

// First
func First(where interface{}, out interface{}) (notFound bool, err error) {
	err = db.Where(where).First(out).Error
	if err != nil {
		notFound = gorm.IsRecordNotFoundError(err)
	}
	return
}

// Last
func Last(where interface{}, out interface{}) (notFound bool, err error) {
	err = db.Where(where).Last(out).Error
	if err != nil {
		notFound = gorm.IsRecordNotFoundError(err)
	}
	return
}

// Find
func Find(where interface{}, out interface{}, orders ...string) error {
	db := db.Where(where)
	if len(orders) > 0 {
		for _, order := range orders {
			db = db.Order(order)
		}
	}
	return db.Find(out).Error
}

// Scan
func Scan(model, where interface{}, out interface{}) (notFound bool, err error) {
	err = db.Model(model).Where(where).Scan(out).Error
	if err != nil {
		notFound = gorm.IsRecordNotFoundError(err)
	}
	return
}

// ScanList
func ScanList(model, where interface{}, out interface{}, orders ...string) error {
	db := db.Model(model).Where(where)
	if len(orders) > 0 {
		for _, order := range orders {
			db = db.Order(order)
		}
	}
	return db.Scan(out).Error
}

// GetPage
func GetPage(model, where interface{}, out interface{}, selectFields []string, pageIndex, pageSize uint64, totalCount *uint64, whereOrder ...PageWhereOrder) error {
	db := db.Model(model).Where(where)
	if len(whereOrder) > 0 {
		for _, wo := range whereOrder {
			if wo.Order != "" {
				db = db.Order(wo.Order)
			}
			if wo.Where != "" {
				db = db.Where(wo.Where, wo.Value...)
			}
		}
	}
	err := db.Count(totalCount).Error
	if err != nil {
		return err
	}
	if *totalCount == 0 {
		return nil
	}
	db = db.Offset((pageIndex - 1) * pageSize).Limit(pageSize)
	if len(selectFields) > 0 {
		db = db.Select(selectFields)
	}
	return db.Find(out).Error
}

// PluckList
func PluckList(model, where interface{}, out interface{}, fieldName string) error {
	return db.Model(model).Where(where).Pluck(fieldName, out).Error
}

// ForUpdate for update
func ForUpdate(dbConn *gorm.DB) *gorm.DB {
	return dbConn.Set("gorm:query_option", "FOR UPDATE")
}

// Pagination : pagination function
func Pagination(dbConn *gorm.DB, pagingOptionCollection *pagination.PagingOptionCollection) *gorm.DB {

	// limit offset
	dbConn = dbConn.Limit(pagingOptionCollection.Limit).Offset(pagingOptionCollection.Offset)

	// where
	for _, where := range pagingOptionCollection.Where {
		// dbConn.Where("columnName = ?", columnData)
		whereString := fmt.Sprintf("%s %s %s", where.Column, where.Symbol, where.Placeholder)
		dbConn = dbConn.Where(whereString, where.Data)
	}

	// order
	for _, order := range pagingOptionCollection.Order {
		dbConn = dbConn.Order(fmt.Sprintf("%s %s", order.Column, order.Direction))
	}
	return dbConn
}
